package com.syf.core.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 * 从 classpath中查找文件
 */
@Slf4j
public class SearchFileFromClassPath {
    private final String relativePath;

    private final Pattern filenamePattern;

    public SearchFileFromClassPath(String relativePath, Pattern filenamePattern) {
        this.relativePath = relativePath;
        this.filenamePattern = filenamePattern;
    }

    public List<FileFindResult> findFilesInClasspath() {
        List<FileFindResult> resultList = new ArrayList<>();

        Enumeration<URL> resources = null;
        try {
//            resources = Thread.currentThread().getContextClassLoader().getResources(relativePath);
            resources = SearchFileFromClassPath.class.getClassLoader().getResources(relativePath);
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();

                if ("file".equalsIgnoreCase(resource.getProtocol())) {
                    Path basePath = Paths.get(resource.toURI());
                    findInFileSystem(basePath, resultList);
                } else if ("jar".equalsIgnoreCase(resource.getProtocol())) {
                    String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!"));
                    try (JarFile jarFile = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()))) {
                        findFilesInJar(jarFile, resultList);
                    }
                }

                //其他类型不处理
            }
        } catch (IOException | URISyntaxException e) {
            throw new RuntimeException(e);
        }

        return resultList;
    }

    private void findInFileSystem(Path targetPath, List<FileFindResult> saveCollect) {
        if (Files.exists(targetPath)) {
            try (Stream<Path> stream = Files.walk(targetPath)) {
                stream.filter(Files::isRegularFile) // 过滤得到是文件的
                        .forEach(path -> {
                            Matcher matcher = filenamePattern.matcher(path.getFileName().toString());
                            if (matcher.matches()) {
                                saveCollect.add(new FileFindResult(IS_FILESYSTEM_RESULT, path.toString()));
                            }
                        });
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void findFilesInJar(JarFile jarFile, List<FileFindResult> saveCollect) throws IOException {
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry jarEntry = entries.nextElement();
            String entryName = jarEntry.getName();
            if (entryName.startsWith(relativePath)) {
                String filename = Paths.get(entryName).getFileName().toString();
                Matcher matcher = filenamePattern.matcher(filename);
                if (matcher.matches()) {
                    saveCollect.add(new FileFindResult(IS_JAR_ENTRY_RESULT, jarEntry.getName(), jarFile.getName()));
                }
            }
        }
    }



    public static final String IS_FILESYSTEM_RESULT = "1";
    public static final String IS_JAR_ENTRY_RESULT = "2";

    /**
     * 搜索结果定义
     */
    public static class FileFindResult {
        private final String fileType;

        private final String searchPath;

        /**
         * 当fileType = 2时，有值
         */
        private final String jarFileName;

        public FileFindResult(String fileType, String searchPath) {
            this(fileType, searchPath, null);
        }

        public FileFindResult(String fileType, String searchPath, String jarFileName) {
            this.fileType = fileType;
            this.searchPath = searchPath;
            this.jarFileName = jarFileName;
        }

        public String getFileType() {
            return fileType;
        }

        public String getSearchPath() {
            return searchPath;
        }

        public String getJarFileName() {
            return jarFileName;
        }

        @Override
        public String toString() {
            return "FileFindResult{" +
                    "fileType='" + fileType + '\'' +
                    ", searchPath='" + searchPath + '\'' +
                    ", jarFileName='" + jarFileName + '\'' +
                    '}';
        }
    }
}
